home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / prog / asm_n_z.arj / SIT.ASM < prev    next >
Assembly Source File  |  1989-10-23  |  18KB  |  652 lines

  1.     ;SIT -- enhanced SET for MS-DOS
  2.     ;
  3.     ;source code and executable placed IN THE PUBLIC DOMAIN by the author:
  4.     ;
  5.     ;  Davidson Corry
  6.     ;  4610 SW Lander
  7.     ;  Seattle, WA    98116
  8.     ;  (206) 935-0244
  9.     ;
  10.     ;support and enhancements are NOT guaranteed in any way, but
  11.     ;  if you have a question, suggestion, or comment, call me up.
  12.     ;  I might be tickled enough to do something about it!
  13.     ;
  14.     ;
  15.     ;Written for Microsoft MASM 5.1
  16.     ;
  17.     ;Use the following commands to create SIT.COM:
  18.     ;
  19.     ;    MASM SIT;
  20.     ;    LINK SIT;         (ignore the warning about NO STACK)
  21.     ;    EXE2BIN SIT
  22.     ;    REN SIT.BIN SIT.COM
  23.     ;
  24.     ;and then you can erase SIT.OBJ and SIT.EXE
  25.     ;
  26.     ;
  27.  
  28.     include    sit.inc ;structured ASM macros, etc.
  29.  
  30.  
  31.  
  32.     ;operation codes
  33. report    equ 1
  34. create    equ 2
  35. remove    equ 3
  36. append    equ 4
  37. prepend    equ 5
  38.  
  39. cseg    segment    byte public
  40.     assume    cs:cseg, ds:cseg, ss:cseg, es:nothing
  41.     ; CS = DS = ES = SS = PSP segment in .COM format programs
  42.  
  43.     org    100h ;.COM format executable
  44.  
  45. ;; ============= Primary routine
  46.  
  47. main:
  48.     call    locate_master_environment
  49.     call    clean_up_command_line
  50.     _if    <cmp byte ptr ds:[80h],0>,ne ;there ARE command line arguments
  51.         xor    cx,cx
  52.         mov    cl,ds:[80h]
  53.         inc    cx         ;copy command line INCLUDING terminating null
  54.         mov    ax,cs
  55.         mov    es,ax
  56.         mov    si,81h
  57.         mov    di,offset line_buffer
  58.         cld
  59.         rep    movsb
  60.         xor    ax,ax
  61.         stosb             ;terminate with a null
  62.         call    process_line
  63.     _else                 ;there are NO command line arguments, read lines from Standard Input
  64.         _while    <call read_from_stdin>,nc ;UNsuccessful read of a line from STDIN
  65.             call    process_line
  66.         _wend
  67.     _endif
  68.     mov    ax,4c00h         ;terminate program
  69.     dos
  70.  
  71. ;; ============== coded data
  72.  
  73.     public    master_env_seg, master_env_siz
  74.  
  75. master_env_seg    dw 0 ;segment address of master environment copy
  76. master_env_siz    dw 0 ;size (in bytes) of master environment copy
  77.  
  78.     public    variable_size, variable_posn
  79.  
  80. variable_size    dw 0 ;size (in bytes) of resultant VARIABLE=VALUE string
  81. variable_posn    dw 0 ;position (in master environment area) to replace new VARIABLE=VALUE string
  82.  
  83.     public    variable_name_size
  84.  
  85. variable_name_size    dw 0
  86.  
  87.     public    operation
  88.  
  89. operation    db 0 ;which op to perform? remove, change, append, prepend, report?
  90.  
  91.     public    no_room_msg
  92. no_room_msg    db 'Master environment allocation exceeded$'
  93.  
  94.     public    bad_syntax_msg
  95. bad_syntax_msg    db 'Bad syntax.  Usage is VARIABLE { [ | = | += | -= | &= ] VALUE STRING }$'
  96.  
  97.     public    cant_find_command_com_msg
  98. cant_find_command_com_msg    db    'Cannot locate primary command shell, terminating.$'
  99.  
  100.     public    cant_find_master_env_msg
  101. cant_find_master_env_msg       db      'Cannot locate master environment segment, terminating.',cr,lf
  102.     db    '  If you are using 4DOS, you must use the /M:nnn switch -- see 4DOS.DOC$'
  103.  
  104. ;; ============== support routines
  105.  
  106.     public    clean_up_command_line
  107. clean_up_command_line:
  108.     xor    cx,cx
  109.     mov    cl,ds:[80h]         ;CX = size of command line passed to SIT
  110.     xor    dx,dx             ;clear bitflags 01 = "there were nonblank characters"
  111.     mov    si,81h
  112.     _if    ,ncxz             ;if there are some chars on command line...
  113.         _loop
  114.             _if    <cmp byte ptr [si],' '>,a
  115.                 or    dx,01 ;there are nonblank characters in the command line
  116.             _endif
  117.             inc    si
  118.         _nextcx
  119.     _endif
  120.     _if    <test dx,1>,z         ;there were NO non-blank characters in command line, kill command line
  121.         mov    byte ptr ds:[80h],0 ;pretend the command line was empty
  122.     _endif
  123.     mov    si,81h
  124.     xor    bx,bx
  125.     mov    bl,ds:[80h]
  126.     mov    byte ptr [bx+si],0     ;null-terminate command line
  127.     ret
  128.  
  129.     public    read_from_stdin
  130. read_from_stdin:
  131.     mov    dx,offset line_buffer
  132.     _loop
  133.         mov    ah,3fh         ;"read from file handle"
  134.         mov    bx,0         ;STDIN file handle, always open
  135.         mov    cx,1         ;get 1 character
  136.         dos
  137.     _break    <test ax,ax>,z         ;no characters read, we're at end-of-file
  138.         mov    si,dx
  139.     _break    <cmp byte ptr [si],'Z'-'@'>,e ;ctrl-Z is end-of-file too!
  140.  
  141.         ;Input from STDIN may have lines delimited by
  142.         ; CR alone, LF alone, CR/LF or LF/CR (depending on
  143.         ; whether the source is the keyboard or a text file
  144.         ; from any of several kinds of editors.
  145.         ;To handle the re-display consistently, we
  146.         ; 1) treat CR and LF identically
  147.         ; 2) treat CR (or LF) as an end-of-line marker
  148.         ;      ONLY when there are characters in the line buffer,
  149.         ;    otherwise ignore it.
  150.         ; 3) echo all characters up to EOL, then explicitly send
  151.         ;      a CR/LF
  152.  
  153.         _if    <cmp byte ptr [si],lf>,e ;treat LF = CR
  154.             mov    byte ptr [si],cr
  155.         _endif
  156.  
  157.         _if    <cmp byte ptr [si],cr>,e ;possible end-of-line
  158.     _again    <cmp dx,offset line_buffer>,e ;no chars in buffer, ignore CR
  159.             mov    byte ptr [si],0 ;terminate line with null
  160.     _break
  161.         _endif
  162.         inc    dx
  163.     _lend
  164.     ;read from STDIN reached end-of-file or end-of-line, return CARRY if buffer is empty
  165.     _if    <cmp dx,offset line_buffer>,e ;no chars
  166.         stc             ;set CARRY = unsuccessful read
  167.     _else
  168.         clc
  169.     _endif
  170.     ret
  171.  
  172.     public    locate_master_environment
  173. locate_master_environment:
  174.     ;locate master copy of environment
  175.  
  176.     ;first step is to locate the master copy of COMMAND.COM
  177.     ; (or whatever command shell is in charge)
  178.     ;This is the first memory block that "owns itself" --
  179.     ; that is, the segment of the arena header is one less than
  180.     ; the segment of the PSP that owns it
  181.  
  182.     ;see Ray Duncan's ADVANCED MS-DOS for an excellent discussion
  183.     ; of memory allocation and arena headers in MS-DOS
  184.  
  185.     ;NOTE *** in version 1.0 of SIT, I assumed that the segment portion of
  186.     ;    the INT 2Eh vector pointed to the master COMMAND.COM segment --
  187.     ;    that turned out to be true for Microsoft COMMAND.COM, but
  188.     ;    not for the (excellent) shareware replacement 4DOS --
  189.     ; The following method is more robust, and correctly finds both
  190.     ;    COMMAND.COM and 4DOS.
  191.  
  192.     mov    ah,52h
  193.     dos                 ;undocumented DOS call, returns ES:BX -> "list of lists"
  194.     mov    ax,es:-2[bx]         ;BX is the segment address of the first DOS memory allocation block
  195.     push    ax             ;save the pointer to the first memory block
  196.     _loop
  197.         mov    es,ax         ;ES -> segaddr of arena header
  198.         inc    ax         ;AX -> segaddr of memory block itself
  199.         cmp    ax,es:[1]     ;see if they're the same
  200.         _if    ,e         ;yes, they're the same, AX is the segaddr of the primary shell COMMAND.COM
  201.             mov    di,ax     ;save it in AX for next loop
  202.     _break
  203.         _endif
  204.         _if    <cmp    byte ptr es:[0],'Z'>,e    ;reached end of arena header chain, COMMAND.COM not found (impossible, but true...)
  205.             mov    dx,offset cant_find_command_com_msg
  206.             mov    ah,9
  207.             dos
  208.             mov    ax,4cffh    ;quit with errorlevel 255
  209.             dos
  210.         _endif
  211.         add    ax,es:[3]     ;advance AX -> next arena header
  212.     _lend
  213.     pop    ax             ;restore segment address of first memory block
  214.  
  215.     _loop
  216.         mov    es,ax         ;ES segment is a memory allocation block ("arena header")
  217.  
  218.         ;see who "owns" this block of memory -- the PSP segment
  219.         ; of the owner is the value in the word at ES:[1]
  220.  
  221.         _if    <cmp di,es:[1]>,e ;master COMMAND.COM owns it, see if it's the master environment
  222.             _if    <cmp word ptr es:[3],3>,a ;3-paragraph block is the CURRENTLY-RUNNING PROGRAM block
  223.                 inc    ax ;AX is segment address of memory block ITSELF (rather than its arena header)
  224.                 cmp    di,ax ;compare to COMMAND.COM PSP segment value
  225.                 mov    ax,es ;restore AX -> arena header, but doesn't affect flags from CMP DI,BX
  226.  
  227.                 ;The first three memory blocks owned by the master copy of COMMAND.COM are usually
  228.                 ;  1) COMMAND.COM itself,
  229.                 ;  2) a 48-byte block containing the name of the
  230.                 ;    currently-executing program, and
  231.                 ;  3) the master environment block
  232.                 ;
  233.                 ;We can tell them apart because the arena header for COMMAND.COM will be
  234.                 ;  immediately preceding the memory block for COMMAND.COM (segment pointed by DI),
  235.                 ;  while the arena header for the master environment strings block will
  236.                 ;  be somewhere else.
  237.                 _if    ,ne ;arena header did NOT immediately precede the DI segment, we've found master environment block
  238.                     inc    ax ;AX -> segment of environment block
  239.                     mov    master_env_seg,ax
  240.                     mov    ax,es:[3] ;size of memory block in paragraphs
  241.                     shl    ax,1
  242.                     shl    ax,1
  243.                     shl    ax,1
  244.                     shl    ax,1 ;AX = size of memory block in BYTES
  245.                     mov    master_env_siz,ax
  246.                     mov    ax,cs
  247.                     mov    es,ax ;restore ES = local segment
  248.                     ret ;done, return to main routine
  249.                 _endif
  250.             _endif
  251.         _endif
  252.  
  253.         _if    <cmp    byte ptr es:[0],'Z'>,e    ;reached end of arena header chain, COMMAND.COM not found (impossible, but true...)
  254.             mov    dx,offset cant_find_master_env_msg
  255.             mov    ah,9
  256.             dos
  257.             mov    ax,4cffh    ;quit with errorlevel 255
  258.             dos
  259.         _endif
  260.  
  261.         ;wrong block, move AX to point to next block
  262.         add    ax,es:[3]     ;size of memory block (in paragraphs)
  263.         inc    ax         ;plus one, to compensate for the arena header paragraph itself
  264.         ;AX now is the segment address of the next arena header
  265.     _lend
  266.  
  267.     public    process_line
  268. process_line:
  269.     _if    <call parse_line>,c     ;break line up into (variable_name) (op) (value)
  270.         mov    dx,offset bad_syntax_msg
  271.         mov    ah,9
  272.         dos
  273.         mov    ax,4c02h     ;errorlevel 2 abort
  274.         dos
  275.     _endif
  276.  
  277.     _if    <call extract_variable>,c ;unsuccessful extraction, no operation
  278.         ret
  279.     _endif
  280.  
  281.     _if    <cmp operation,report>,e
  282.         call    v_report
  283.     _elseif    <cmp operation,remove>,e
  284.         call    v_remove
  285.     _elseif    <cmp operation,append>,e
  286.         call    v_append
  287.     _elseif    <cmp operation,prepend>,e
  288.         call    v_prepend
  289.     _endif
  290.     ;else operation is CREATE, which is done below
  291.  
  292.     ;compute size of new string
  293.     mov    si,offset new_value
  294.     mov    cx,variable_name_size     ;size of "VARIABLE=" portion
  295.     cld
  296.     _loop
  297.         lodsb
  298.         inc    cx
  299.     _until    <test al,al>,z
  300.     mov    variable_size,cx     ;size of "VARIABLE=VALUE" string with terminating null byte
  301.     mov    ax,variable_posn
  302.     add    ax,cx
  303.     inc    ax             ;compensate for terminating null byte on environment ("last null item")
  304.     _if    <cmp ax,master_env_siz>,a
  305.         mov    dx,offset no_room_msg
  306.         mov    ah,9
  307.         dos
  308.         mov    ax,4c01h     ;errorlevel 1 abort
  309.         dos
  310.     _endif
  311.     ;otherwise, there is room for a successful operation
  312.     ; copy VARIABLE=VALUE characters to STDOUT
  313.     mov    si,offset variable_name
  314.     _loop
  315.         mov    dl,[si]
  316.     _break    <test dl,dl>,z
  317.         inc    si
  318.         push    si
  319.         mov    ah,2
  320.         dos
  321.         pop    si
  322.     _lend
  323.     mov    si,offset new_value
  324.     _loop
  325.         mov    dl,[si]
  326.     _break    <test dl,dl>,z
  327.         inc    si
  328.         push    si
  329.         mov    ah,2
  330.         dos
  331.         pop    si
  332.     _lend
  333.     mov    dl,cr
  334.     mov    ah,2
  335.     dos
  336.     mov    dl,lf
  337.     mov    ah,2
  338.     dos
  339.  
  340.     ; copy VARIABLE=VALUE string to master environment
  341.     _if    <cmp byte ptr [new_value],0>,ne
  342.         mov    ax,master_env_seg
  343.         mov    es,ax
  344.         mov    di,variable_posn
  345.         mov    si,offset variable_name
  346.         cld
  347.         _loop
  348.             lodsb
  349.         _break    <test al,al>,z     ;done with VARIABLE=, now copy NEW_VALUE
  350.             stosb
  351.         _lend
  352.         mov    si,offset new_value
  353.         _loop
  354.             lodsb
  355.             stosb
  356.         _until    <test al,al>,z
  357.         stosb             ;extra null to terminate ENVIRONMENT strings
  358.         mov    ax,cs
  359.         mov    es,ax         ;restore ES = local segment
  360.     _endif
  361.     ret
  362.  
  363.     public    parse_line
  364. parse_line:
  365.     ;find first nonblank char in line
  366.     mov    si,offset line_buffer
  367.     _while    <cmp byte ptr [si],0>,a
  368.     _break    <cmp byte ptr [si],' '>,a ;break at first non-blank
  369.         inc    si
  370.     _wend
  371.     _if    <cmp byte ptr [si],0>,e     ;no variable name before (endofline)
  372.         stc
  373.         ret
  374.     _endif
  375.     mov    dx,si             ;DX -> first non-blank in string
  376.  
  377.     ;find end of variable name = (opcode) | (endofline)
  378.     _while    <cmp byte ptr [si],0>,a
  379.     _break    <cmp byte ptr [si],'='>,e
  380.         _if    <cmp byte ptr [si],'&'>,e
  381.     _break    <cmp byte ptr 1[si],'='>,e
  382.         _endif
  383.         _if    <cmp byte ptr [si],'-'>,e
  384.     _break    <cmp byte ptr 1[si],'='>,e
  385.         _endif
  386.         _if    <cmp byte ptr [si],'+'>,e
  387.     _break    <cmp byte ptr 1[si],'='>,e
  388.         _endif
  389.         _if    <cmp byte ptr [si],'&'>,e
  390.     _break    <cmp byte ptr 1[si],'='>,e
  391.         _endif
  392.         inc    si
  393.     _wend
  394.     ;SI -> character PAST end of variable name
  395.  
  396.     ;copy variable name to buffer, upcase, trim trailing spaces, then append '=' sign
  397.     push    si             ;save pointer to first char of (op)
  398.     mov    cx,si             ;pointer past end of VARIABLE
  399.     mov    si,dx             ;pointer to first char of VARIABLE
  400.     sub    cx,dx             ;CX = length of VARIABLE
  401.     mov    di,offset variable_name
  402.     cld
  403.     _if    ,ncxz
  404.         _loop
  405.             lodsb
  406.             _if    <cmp al,'a'>,ae ;if it's a lowercase letter
  407.                 _if    <cmp al,'z'>,be
  408.                     sub    al,'a'-'A' ;UPCASE the letter
  409.                 _endif
  410.             _endif
  411.             stosb
  412.         _nextcx
  413.     _endif
  414.     ;VARIABLE moved, trim trailing spaces
  415.     _while    <cmp di,offset variable_name>,a
  416.     _break    <cmp byte ptr -1[di],' '>,a
  417.         dec    di
  418.     _wend
  419.     pop    si             ;restore pointer to first char of (op)
  420.     _if    <cmp di,offset variable_name>,be ;"Variable_name" is all trailing spaces!
  421.         mov    dx,offset bad_syntax_msg
  422.         mov    ah,9
  423.         dos
  424.         mov    ax,4c02h     ;errorlevel 2 abort
  425.         dos
  426.     _endif
  427.     mov    al,'='
  428.     stosb
  429.     mov    ax,di
  430.     sub    ax,offset variable_name
  431.     mov    variable_name_size,ax
  432.     xor    ax,ax
  433.     stosb
  434.  
  435.     ;now skip over (and mark type of) operator string
  436.     mov    operation,report     ;default op is "report current value"
  437.     _if    <cmp byte ptr [si],'='>,e ;"create new" VARIABLE=STRING
  438.         inc    si         ;skip over '='
  439.         mov    operation,create
  440.     _elseif    <cmp byte ptr [si],'-'>,e ;remove substring
  441.         inc    si         ;skip over '-='
  442.         inc    si
  443.         mov    operation,remove
  444.     _elseif    <cmp byte ptr [si],'+'>,e ;append substring
  445.         inc    si         ;skip over '+='
  446.         inc    si
  447.         mov    operation,append
  448.     _elseif    <cmp byte ptr [si],'&'>,e ;prepend substring
  449.         inc    si         ;skip over '&='
  450.         inc    si
  451.         mov    operation,prepend
  452.     _endif
  453.  
  454.     ;finally, copy remaining string to NEW_VALUE
  455.     mov    di,offset new_value
  456.     _loop
  457.         lodsb
  458.         stosb
  459.     _until    <test al,al>,z
  460.     ret
  461.  
  462.     public    extract_variable
  463. extract_variable:
  464.     mov    ax,master_env_seg
  465.     mov    ds,ax
  466.     mov    si,0             ;DS:SI -> first char of first string in environment
  467.     _if    <cmp byte ptr [si],0>,e     ;environment is EMPTY (impossible according to DOS docs, but valid programming practice anyway)
  468.         mov    ax,cs
  469.         mov    ds,ax         ;restore DS = local segment
  470.         stc             ;set carry to indicate EMPTY ENVIRONMENT
  471.         ret
  472.     _endif
  473.     _loop
  474.         _if    <cmp byte ptr [si],0>,e ;reached end of environment without finding item,
  475.             ;treat as though ITEM exists at end of environment with empty value
  476.             mov    ax,cs
  477.             mov    ds,ax     ;restore DS = local segment
  478.             mov    variable_posn,si ;this is where variable will go
  479.             mov    byte ptr old_value,0 ;empty string is "old value"
  480.             clc         ;carry CLEAR indicates "variable found"
  481.             ret
  482.         _endif
  483.         mov    di,offset variable_name
  484.         mov    cx,es:variable_name_size
  485.         push    si
  486.         rep    cmpsb
  487.         pop    si
  488.     _break    ,z             ;successful match found at DS:SI->
  489.         ;unsuccessful, find end of this environment item
  490.         _loop
  491.             lodsb
  492.         _until    <test al,al>,z     ;null terminates THIS env. item
  493.     _lend
  494.     ;DS:SI -> matched env item, "extract" it:
  495.     ;  copy the (existing) value to OLD_VALUE,
  496.     ;  then "bump down" all subsequent items in master environment.
  497.     ;The modified VARIABLE=VALUE string will be written back
  498.     ;  to the master environment at the END of the environment.
  499.     push    si
  500.     add    si,es:variable_name_size
  501.     mov    di,offset old_value
  502.     _loop
  503.         lodsb
  504.         stosb
  505.     _until    <test al,al>,z         ;copy INCLUDING null terminator
  506.     ;now copy down all subsequent env. strings
  507.     mov    ax,ds             ; DS = ES = master environment segment
  508.     mov    es,ax
  509.     ;DS:SI -> start of NEXT environment string
  510.     pop    di             ;ES:DI -> start of EXTRACTED environment string
  511.     _loop
  512.         lodsb
  513.         stosb
  514.     _break    <test al,al>,z         ;TERMINATING null "environment item"
  515.         _loop
  516.             lodsb
  517.             stosb
  518.         _until    <test al,al>,z     ;copy environment item until terminating null
  519.     _lend
  520.     dec    di             ;restore pointer to terminating "null env. item", for later append of revised VARIABLE=VALUE string
  521.     mov    ax,cs             ;restore data/extra segments to our .COM segment
  522.     mov    ds,ax
  523.     mov    es,ax
  524.     mov    variable_posn,di     ;save the pointer (offset in ENV segment)
  525.     clc                 ;clear carry indicates SUCCESSFUL EXTRACTION
  526.     ret
  527.  
  528.     public    v_remove
  529. v_remove:
  530.     ;find length of NEW_VALUE
  531.     mov    si,offset new_value
  532.     _while    <cmp byte ptr [si],0>,ne
  533.         inc    si
  534.     _wend
  535.     sub    si,offset new_value
  536.     mov    cx,si             ;CX = length of NEW_VALUE string
  537.  
  538.     ;find NEW_VALUE within OLD_VALUE (if it's there)
  539.     mov    di,offset old_value
  540.     mov    si,offset new_value
  541.     cld
  542.     _loop
  543.         cmp    byte ptr [di],0
  544.         je    v_report     ;didn't find substring, just REPORT existing value
  545.         push    di
  546.         push    si
  547.         push    cx
  548.         repe    cmpsb         ;compare the substrings for CX bytes
  549.         pop    cx
  550.         pop    si
  551.         pop    di
  552.     _break    ,e             ;substring found at [DI]
  553.         inc    di
  554.     _lend
  555.     mov    si,di
  556.     add    si,cx             ;SI -> past found substring
  557.     cld                 ;copy down trailing string, to trailing null
  558.     _loop
  559.         lodsb
  560.         stosb
  561.     _until    <test al,al>,z
  562.     ;now desired value is in OLD_VALUE, just do a V_REPORT
  563.  
  564.     ;deliberate fallthrough to V_REPORT
  565.  
  566.     public    v_report
  567. v_report:
  568.     ;"report" is just (copy old value to new value) then (create new value)
  569.     mov    si,offset old_value
  570.     mov    di,offset new_value
  571.     cld
  572.     _loop
  573.         lodsb
  574.         stosb
  575.     _until    <test al,al>,z
  576.     ret
  577.  
  578.     public    v_append
  579. v_append:
  580.     ;copy NEW_VALUE to workspace
  581.     mov    si,offset new_value
  582.     mov    di,offset workspace
  583.     cld
  584.     _loop
  585.         lodsb
  586.         stosb
  587.     _until    <test al,al>,z
  588.     ;copy OLD_VALUE to NEWVALUE without trailing null
  589.     mov    si,offset old_value
  590.     mov    di,offset new_value
  591.     cld
  592.     _loop
  593.         lodsb
  594.     _break    <test al,al>,z
  595.         stosb
  596.     _lend
  597.     ;now continue copy from WORKSPACE
  598.     mov    si,offset workspace
  599.     cld
  600.     _loop
  601.         lodsb
  602.         stosb
  603.     _until    <test al,al>,z
  604.     ret
  605.  
  606.     public    v_prepend
  607. v_prepend:
  608.     ;find end of NEW_VALUE
  609.     mov    di,offset new_value
  610.     cld
  611.     _while    <cmp byte ptr [di],0>,ne
  612.         inc    di
  613.     _wend
  614.     ;then tack OLD_VALUE on the end
  615.     mov    si,offset old_value
  616.     cld
  617.     _loop
  618.         lodsb
  619.         stosb
  620.     _until    <test al,al>,z
  621.     ret
  622.  
  623.  
  624.  
  625.  
  626.  
  627.  
  628.  
  629.  
  630. ;; ============= buffer areas
  631.  
  632.     ;by defining these buffers as LABELs, we don't include them in the
  633.     ; actual .COM file as zero data.
  634.  
  635.     public    line_buffer
  636. line_buffer    label byte
  637.  
  638.     public    variable_name
  639. variable_name    equ line_buffer + 512
  640.  
  641.     public    old_value
  642. old_value    equ variable_name + 512
  643.  
  644.     public    new_value
  645. new_value    equ old_value + 512
  646.  
  647.     public    workspace
  648. workspace    equ new_value + 512
  649.  
  650. cseg    ends
  651.     end    main
  652.